package com.heima.article.service.Impl;


import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.heima.article.mapper.ApArticleConfigMapper;
import com.heima.article.mapper.ApArticleContentMapper;
import com.heima.article.mapper.ApArticleMapper;
import com.heima.article.service.ApArticleService;
import com.heima.article.service.ArticleFreemarkerService;
import com.heima.common.constants.ArticleConstants;
import com.heima.common.constants.BehaviorConstants;
import com.heima.common.redis.CacheService;
import com.heima.model.article.dtos.ApUserLoadDto;
import com.heima.model.article.dtos.ArticleCommentDto;
import com.heima.model.article.dtos.ArticleDto;
import com.heima.model.article.dtos.ArticleHomeDto;
import com.heima.model.article.mess.ArticleVisitStreamMess;
import com.heima.model.article.pojos.ApArticle;
import com.heima.model.article.pojos.ApArticleConfig;
import com.heima.model.article.pojos.ApArticleContent;
import com.heima.model.article.vos.ArticleCommentVo;
import com.heima.model.article.vos.HotArticleVo;
import com.heima.model.comment.dtos.ApCommentDto;
import com.heima.model.common.dtos.PageResponseResult;
import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.common.enums.AppHttpCodeEnum;
import com.heima.model.user.pojos.ApUser;
import com.heima.model.wemedia.dtos.StatisticsDto;
import com.heima.utils.common.DateUtils;
import com.heima.utils.thread.ApThreadLocalUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

@Service
public class ApArticleServiceImpl extends ServiceImpl<ApArticleMapper, ApArticle> implements ApArticleService {

    @Autowired
    private ApArticleMapper apArticleMapper;
    @Autowired
    private ApArticleConfigMapper apArticleConfigMapper;

    @Autowired
    private ApArticleContentMapper apArticleContentMapper;

    @Autowired
    private ArticleFreemarkerService articleFreemarkerService;

    @Autowired
    private CacheService cacheService;

    //加载App文章列表
    @Override
    public ResponseResult load(ArticleHomeDto dto, Short type) {

        //参数校验：
        //dto , type没有值：返回参数异常:缺少参数
        if (dto == null && type == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_REQUIRE);
        }
        //type 值不符合规定：0 ，1 ：给默认值  1 :加载首页（更多）
        if (!type.equals(ArticleConstants.LOADTYPE_LOAD_MORE) && !type.equals(ArticleConstants.LOADTYPE_LOAD_NEW)) {
            type = ArticleConstants.LOADTYPE_LOAD_MORE;
        }
        //分页Size无值或者值 <= 0：给默认值：10   有值过大用：Math.min  比较 获取 两个数的小值
        if (dto.getSize() == null || dto.getSize() <= 0) {
            dto.setSize(10);
        } else {
            //有值：与 50 比较，获取小的值
            dto.setSize(Math.min(50, dto.getSize()));
        }
        //频道 ID 没有值：给默认值：”__all__“
        if (StringUtils.isEmpty(dto.getTag())) {
            dto.setTag(ArticleConstants.DEFAULT_TAG);
        }
        //最小时间 或 最大时间 无值： 给当前时间
        if (dto.getMaxBehotTime() == null) {
            dto.setMaxBehotTime(new Date());
        }
        if (dto.getMinBehotTime() == null) {
            dto.setMinBehotTime(new Date());
        }

        //查询：
        List<ApArticle> apArticles = apArticleMapper.loadArticleList(dto, type);

        //结果返回
        return ResponseResult.okResult(apArticles);
    }

    /**
     * app端保存文章
     *
     * @param dto
     * @return
     */
    @Override
    public ResponseResult saveArticle(ArticleDto dto) {
        //1.检查参数
        if (dto == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }

        ApArticle apArticle = new ApArticle();
        BeanUtils.copyProperties(dto, apArticle);

        //2.判断是否存在id
        if (dto.getId() == null) {
            //2.1 不存在id  保存  文章  文章配置  文章内容

            //保存文章
            save(apArticle);

            //保存配置
            ApArticleConfig apArticleConfig = new ApArticleConfig(apArticle.getId());
            apArticleConfigMapper.insert(apArticleConfig);

            //保存 文章内容
            ApArticleContent apArticleContent = new ApArticleContent();
            apArticleContent.setArticleId(apArticle.getId());
            apArticleContent.setContent(dto.getContent());
            apArticleContentMapper.insert(apArticleContent);

        } else {
            //2.2 存在id   修改  文章  文章内容

            //修改  文章
            updateById(apArticle);

            //修改文章内容
            ApArticleContent apArticleContent = apArticleContentMapper.selectOne(Wrappers.<ApArticleContent>lambdaQuery().eq(ApArticleContent::getArticleId, dto.getId()));
            apArticleContent.setContent(dto.getContent());
            apArticleContentMapper.updateById(apArticleContent);
        }

        //调用生成静态Html文件：
        articleFreemarkerService.buildArticleToMinIO(apArticle, dto.getContent());

        //3.结果返回  文章的id
        return ResponseResult.okResult(apArticle.getId());
    }


    /**
     * 用户行为数据回显
     *
     * @param dto
     * @return
     */
    @Override
    public ResponseResult ApUserLoad(ApUserLoadDto dto) {

        //校验参数
        if (dto == null || dto.getArticleId() == null || dto.getAuthorId() == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }

        //初始化：是否关注，是否点赞，是否不喜欢，是否收藏： 设置为 否
        boolean isfollow = false, islike = false, isunlike = false, iscollection = false;

        //获取用户Id
        ApUser user = ApThreadLocalUtils.getUser();

        //判断当前是否为正常登录
        if (user != null) {
            //  查询关注Redis  判断是否关注：
            Double score = cacheService.zScore(BehaviorConstants.APUSER_FOLLOW_RELATION + user.getId(),
                    dto.getAuthorId().toString());
            // 不为空：则表示已经关注
            if (score != null) {
                isfollow = true;
            }

            //是否点赞：
            String likeBehaviorJson = (String) cacheService.hGet(BehaviorConstants.LIKE_BEHAVIOR + dto.getArticleId().toString(),
                    user.getId().toString());
            //不为空：以点赞
            if (StringUtils.isNotBlank(likeBehaviorJson)) {
                islike = true;
            }
            //是否不喜欢
            String unLikeBehaviorJson = (String) cacheService.hGet(BehaviorConstants.UN_LIKE_BEHAVIOR + dto.getArticleId().toString(),
                    user.getId().toString());
            //不为空： 不喜欢
            if (StringUtils.isNotBlank(unLikeBehaviorJson)) {
                isunlike = true;
            }
            //是否收藏
            String collctionJson = (String) cacheService.hGet(BehaviorConstants.COLLECTION_BEHAVIOR + user.getId(),
                    dto.getArticleId().toString());
            //不为空：已收藏
            if (StringUtils.isNotBlank(collctionJson)) {
                iscollection = true;
            }
        }

        //封装数据
        Map<String, Object> resultMap = new HashMap<>();
        resultMap.put("isfollow", isfollow);
        resultMap.put("islike", islike);
        resultMap.put("isunlike", isunlike);
        resultMap.put("iscollection", iscollection);

        return ResponseResult.okResult(resultMap);
    }

    /**
     * 加载文章首页
     *
     * @param dto
     * @param type
     * @return
     */
    @Override
    public ResponseResult loadFirst(ArticleHomeDto dto, Short type) {
        //首页：优先推荐缓存中的热点文章数据
        //如果缓存中没有，那再去查数据库
        String articleJson = cacheService.get(ArticleConstants.HOT_ARTICLE_FIRST_PAGE + dto.getTag());
        if (StringUtils.isNotEmpty(articleJson)) {
            List<HotArticleVo> hotArticleVoList = JSON.parseArray(articleJson, HotArticleVo.class);
            return ResponseResult.okResult(hotArticleVoList);
        } else {
            return load(dto, type);
        }
    }


    /**
     * kafkaStream流 : 实时更新数据
     *
     * @param mess
     */
    @Override
    public void updateScore(ArticleVisitStreamMess mess) {
        //获取本次更新同步之前的分数：
        ApArticle oldApArticle = apArticleMapper.selectById(mess.getArticleId());
        //计算此次之前的分数：
        Integer odlScore = computeScore(oldApArticle);

        //将本次时间窗口获得的点赞等操作 累加后 更新：
        ApArticle newApArticle = updateArticle(mess);
        //计算更新后的总得分：
        Integer newScore = computeScore(newApArticle);
        Integer allScore = odlScore + (newScore - odlScore) * 3;

        //更新频道的热点文章：
        replaceDataToRedis(newApArticle, allScore, ArticleConstants.HOT_ARTICLE_FIRST_PAGE + newApArticle.getChannelId());

        //更新推荐的热点文章：
        replaceDataToRedis(newApArticle, allScore, ArticleConstants.HOT_ARTICLE_FIRST_PAGE + ArticleConstants.DEFAULT_TAG);

    }

    /**
     * 替换数据并且存入到redis
     *
     * @param newApArticle
     * @param allScore
     * @param redisKey
     */
    private void replaceDataToRedis(ApArticle newApArticle, Integer allScore, String redisKey) {
        //根据key获取到redis中的数据
        String hotArticleVoListStr = cacheService.get(redisKey);
        //校验：
        if (StringUtils.isNotEmpty(hotArticleVoListStr)) {
            //转回集合：
            List<HotArticleVo> hotArticleVoList = JSON.parseArray(hotArticleVoListStr, HotArticleVo.class);

            boolean flag = true;

            //判断当前文章是否已经存在redis中，如果存在更新分数：
            for (HotArticleVo hotArticleVo : hotArticleVoList) {
                if (hotArticleVo.getId().equals(newApArticle.getId())) {
                    //存在更新分数：
                    hotArticleVo.setScore(allScore);
                    flag = false;
                    break;
                }
            }

            //不存在：分数倒叙，与最后一个比较分数： 比最后一个大则替换：
            if (flag) {
                //如果缓存的数据 大于等于 5.在进行比较，否则直接添加：
                if (hotArticleVoList.size() >= 5) {
                    //排序： 根据分数倒叙：
                    hotArticleVoList = hotArticleVoList.stream().sorted(((o1, o2) -> {
                        return o2.getScore() - o1.getScore();
                    })).collect(Collectors.toList());
                    //获取最后一个：
                    HotArticleVo lastHotArticleVo = hotArticleVoList.get(hotArticleVoList.size() - 1);
                    //比较：如果小于，替换掉
                    if (lastHotArticleVo.getScore() < allScore) {
                        hotArticleVoList.remove(lastHotArticleVo);
                        //将文章封装成Vo储存
                        HotArticleVo hotArticleVo = new HotArticleVo();
                        BeanUtils.copyProperties(newApArticle, hotArticleVo);
                        hotArticleVo.setScore(allScore);

                        hotArticleVoList.add(hotArticleVo);
                    }
                } else {
                    //缓存数据不足 5  直接添加：
                    //将文章封装成Vo储存
                    HotArticleVo hotArticleVo = new HotArticleVo();
                    BeanUtils.copyProperties(newApArticle, hotArticleVo);
                    hotArticleVo.setScore(allScore);

                    hotArticleVoList.add(hotArticleVo);
                }
            }

            //缓存到redis
            hotArticleVoList = hotArticleVoList.stream()
                    .sorted(Comparator.comparing(HotArticleVo::getScore)
                            .reversed()).collect(Collectors.toList());
            cacheService.set(redisKey, JSON.toJSONString(hotArticleVoList));
        }
    }


    /**
     * 更新文章行为数量
     *
     * @param mess
     */
    private ApArticle updateArticle(ArticleVisitStreamMess mess) {
        ApArticle apArticle = getById(mess.getArticleId());
        apArticle.setCollection(apArticle.getCollection() == null ? mess.getCollect() : apArticle.getCollection() + mess.getCollect());
        apArticle.setComment(apArticle.getComment() == null ? mess.getComment() : apArticle.getComment() + mess.getComment());
        apArticle.setLikes(apArticle.getLikes() == null ? mess.getLike() : apArticle.getLikes() + mess.getLike());
        apArticle.setViews(apArticle.getViews() == null ? mess.getView() : apArticle.getViews() + mess.getView());
        updateById(apArticle);
        return apArticle;

    }


    /**
     * 计算文章的具体分值
     *
     * @param apArticle
     * @return
     */
    private Integer computeScore(ApArticle apArticle) {
        Integer score = 0;
        if (apArticle.getLikes() != null) {
            score += apArticle.getLikes() * ArticleConstants.HOT_ARTICLE_LIKE_WEIGHT;
        }
        if (apArticle.getViews() != null) {
            score += apArticle.getViews();
        }
        if (apArticle.getComment() != null) {
            score += apArticle.getComment() * ArticleConstants.HOT_ARTICLE_COMMENT_WEIGHT;
        }
        if (apArticle.getCollection() != null) {
            score += apArticle.getCollection() * ArticleConstants.HOT_ARTICLE_COLLECTION_WEIGHT;
        }

        return score;
    }

    /**
     * 图文统计统计
     * @param wmUserId
     * @param beginDate
     * @param endDate
     * @return
     */
    @Override
    public ResponseResult queryLikesAndConllections(Integer wmUserId, Date beginDate, Date endDate) {
        Map map = apArticleMapper.queryLikesAndConllections(wmUserId, beginDate, endDate);
        return ResponseResult.okResult(map);
    }

    /**
     * 分页查询 图文统计
     * @param dto
     * @return
     */
    @Override
    public PageResponseResult newPage(StatisticsDto dto) {
        //类型转换
        Date beginDate = DateUtils.stringToDate(dto.getBeginDate());
        Date endDate = DateUtils.stringToDate(dto.getEndDate());
        //检查参数
        dto.checkParam();
        //分页查询
        IPage page = new Page(dto.getPage(), dto.getSize());
        LambdaQueryWrapper<ApArticle> lambdaQueryWrapper = Wrappers.<ApArticle>lambdaQuery()
                .eq(ApArticle::getAuthorId, dto.getWmUserId())
                .between(ApArticle::getPublishTime,beginDate, endDate)
                .select(ApArticle::getId,ApArticle::getTitle,ApArticle::getLikes,ApArticle::getCollection,ApArticle::getComment,ApArticle::getViews);

        lambdaQueryWrapper.orderByDesc(ApArticle::getPublishTime);

        page = page(page,lambdaQueryWrapper);

        PageResponseResult responseResult = new PageResponseResult(dto.getPage(),dto.getSize(),(int)page.getTotal());
        responseResult.setData(page.getRecords());

        return responseResult;
    }

    /**
     * 查询文章评论统计
     *
     * @param dto
     * @return
     */
    @Override
    public PageResponseResult findNewsComments(ArticleCommentDto dto) {
        Integer currentPage = dto.getPage();
        //数据库查询的起始索引
        dto.setPage((dto.getPage()-1)*dto.getSize());
        //符合条件的文章评论信息查询
        List<ArticleCommentVo> list = apArticleMapper.findNewsCommnets(dto);
        //符合条件的文章的总条数
        int count = apArticleMapper.findNewsCommentsCount(dto);

        PageResponseResult responseResult = new PageResponseResult(currentPage, dto.getSize(), count);
        responseResult.setData(list);
        return responseResult;
    }

    /**
     * 删除评论数,点赞数
     * @param dto
     * @return
     */
    @Override
    public ResponseResult delChannelState(ApCommentDto dto) {
        //1.校验参数
        if (dto == null){
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }
        //2.查询数据库
        ApArticle apArticle = apArticleMapper.selectById(dto.getArticleId());
        if (apArticle == null){
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }
        //3.数据处理
        apArticle.setLikes(0);
        apArticle.setComment(0);
        //4.保存数据库
        apArticleMapper.updateById(apArticle);
        return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
    }

    /**
     * 保存文章评论数
     * @param entryId
     */
    @Override
    public ResponseResult updateComment(Long entryId) {
        //1.校验参数
        if (entryId == null){
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }
        //2.查询数据库
        ApArticle apArticle = apArticleMapper.selectById(entryId);
        if (apArticle == null){
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }

        //3.数据处理
        apArticle.setComment(apArticle.getComment() + 1);
        //4.保存数据库
        apArticleMapper.updateById(apArticle);
        return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
    }
}
